from pathlib import Path
from typing import TYPE_CHECKING, Union, Optional, List

from requests import get, exceptions

from encrypt.msToken import MsToken
from encrypt.ttWid import TtWid
from manager.recorder import DownloadRecorder
from src.config.settings import Settings
from src.custom.internal import USERAGENT
from src.module.ffmpeg import FFMPEG
from src.tools.cleaner import Cleaner
from src.tools.format import cookie_dict_to_str

if TYPE_CHECKING:
    from src.module.cookie import Cookie


class Parameter:
    name_keys = (
        "id",
        "desc",
        "create_time",
        "nickname",
        "uid",
        "mark",
        "type",
    )
    mode_complete_values = {
        "1",
        "2",
        "3",
        "4",
        "4 1",
        "4 2",
        "4 2 1",
        "4 2 2",
        "4 2 3",
        "4 3",
        "4 3 1",
        "4 3 2",
        "4 4",
        "4 5",
        "4 5 1",
        "4 5 2",
        "4 6",
        "4 6 1",
        "4 6 2",
        "4 6 3",
        "4 7",
        "4 7 1",
        "4 7 2",
        "4 7 3",
        "4 8",
        "4 9",
        "4 10",
        "5",
        "6",
        "7",
        "8",
    }
    mode_reduced_values = {
        "1",
        "2",
        "3",
        "4",
        "4 1",
        "4 2",
        "4 2 1",
        "4 2 2",
        "4 2 3",
        "4 3",
        "4 3 1",
        "4 3 2",
        "4 4",
        "4 5",
        "4 5 1",
        "4 5 2",
        "4 5 3",
        "4 6",
        "4 7",
        "5",
        "6",
        "7",
        "8",
    }
    cleaner = Cleaner()

    def __init__(
            self,
            settings: "Settings",
            cookie_object: "Cookie",
            main_path: Path,
            xb,
            blacklist: "DownloadRecorder",
            proxies="",
            max_retry=5,
            timeout=10,
            date_format="%Y-%m-%d %H:%M:%S",
            download=False,
            max_size=0,
            chunk=1048576,
            root="",
            ffmpeg="",
            split="-",
            original_cover=False,
            dynamic_cover=False,
            music=False,
            folder_name="download",
            name_format="",
            folder_mode=False
    ):
        self.settings = settings
        self.date_format = date_format
        self.cookie_object = cookie_object
        self.blacklist = blacklist
        self.proxies = self.__check_proxies(proxies)
        self.max_retry = max_retry
        self.main_path = main_path  # 项目根路径
        self.cache = main_path.joinpath("cache")  # 缓存路径
        self.temp = self.cache.joinpath("temp")  # 临时文件路径
        self.headers = {
            "User-Agent": USERAGENT,
        }
        self.headers_tiktok = {
            "User-Agent": USERAGENT,
        }
        self.xb = xb
        self.timeout = self.__check_timeout(timeout)
        self.download = download
        self.max_size = self.__check_max_size(max_size)
        self.chunk = self.__check_chunk(chunk)
        self.root = self.__check_root(root)
        self.ffmpeg = self.__generate_ffmpeg_object(ffmpeg)
        self.split = split
        self.original_cover = original_cover
        self.dynamic_cover = dynamic_cover
        self.music = music
        self.folder_name = self.__check_folder_name(folder_name)
        self.name_format = self.__check_name_format(name_format)
        self.folder_mode = self.__check_bool(folder_mode)
        self.check_rules = {
            "accounts_urls": None,
            "mix_urls": None,
            "owner_url": None,
            "accounts_urls_tiktok": None,
            "mix_urls_tiktok": None,
            "owner_url_tiktok": None,
            # "folder_mode": self.__check_bool,
            "ffmpeg": self.__generate_ffmpeg_object,
        }


    def __check_folder_name(self, folder_name: str) -> str:
        if folder_name := self.cleaner.filter_name(folder_name, False):
            print(f"folder_name 参数已设置为 {folder_name}")
            return folder_name
        print(f"folder_name 参数 {folder_name} 不是有效的文件夹名称，程序将使用默认值：Download")
        return "Download"

    def __check_name_format(self, name_format: str) -> List[str]:
        name_keys = name_format.strip().split(" ")
        if all(i in self.name_keys for i in name_keys):
            print(f"name_format 参数已设置为 {name_format}")
            return name_keys
        else:
            print(f"name_format 参数 {name_format} 设置错误，程序将使用默认值：创建时间 作品类型 账号昵称 作品描述")
            return ["create_time", "type", "nickname", "desc"]

    @staticmethod
    def __generate_ffmpeg_object(ffmpeg_path: str) -> FFMPEG:
        return FFMPEG(ffmpeg_path)

    def __check_root(self, root: str) -> Path:
        if not root:
            return self.main_path
        if (r := Path(root)).is_dir():
            print(f"root 参数已设置为 {root}")
            return r
        if r := self.__check_root_again(r):
            print(f"root 参数已设置为 {r}")
            return r
        print(f"root 参数 {root} 不是有效的文件夹路径，程序将使用项目根路径作为储存路径")
        return self.main_path

    @staticmethod
    def __check_chunk(chunk: int) -> int:
        if isinstance(chunk, int) and chunk > 1024:
            print(f"chunk 参数已设置为 {chunk}")
            return chunk
        print(f"chunk 参数 {chunk} 设置错误，程序将使用默认值：{1024 * 1024}")
        return 1024 * 1024

    @staticmethod
    def __check_max_size(max_size: int) -> int:
        max_size = max(max_size, 0)
        print(f"max_size 参数已设置为 {max_size}")
        return max_size

    def __check_proxies(
            self,
            proxies: str,
            url="https://www.baidu.com/") -> dict:
        if isinstance(proxies, str) and proxies:
            proxies_dict = {
                "http": proxies,
                "https": proxies,
                "ftp": proxies,
            }
            try:
                response = get(
                    url,
                    proxies=proxies_dict,
                    timeout=self.timeout)
                if response.status_code==200:
                    print(f"代理 {proxies} 测试成功")
                    return proxies_dict
            except exceptions.ReadTimeout:
                print(f"代理 {proxies} 测试超时")
            except (
                    exceptions.ProxyError,
                    exceptions.SSLError,
                    exceptions.ChunkedEncodingError,
                    exceptions.ConnectionError,
            ):
                print(f"代理 {proxies} 测试失败")
        return {
            "http": None,
            "https": None,
            "ftp": None,
        }

    def __check_timeout(self, timeout: Union[int, float]) -> Union[int, float]:
        if isinstance(timeout, (int, float)) and timeout > 0:
            print(f"timeout 参数已设置为 {timeout}", False)
            return timeout
        print(f"timeout 参数 {timeout} 设置错误，程序将使用默认值：10")
        return 10

    @staticmethod
    def __check_bool(value: bool, default=False) -> bool:
        return value if isinstance(value, bool) else default

    def __check_cookie_tiktok(self, cookie: Union[dict, str], ) -> [dict, str]:
        return self.__check_cookie(cookie, name="cookie_tiktok")

    def __check_cookie(self, cookie: Union[dict, str], name="cookie") -> [dict, str]:
        if isinstance(cookie, dict):
            return cookie, ""
        elif isinstance(cookie, str):
            return {}, cookie
        else:
            print(f"{name} 参数格式错误")
        return {}, ""

    def __get_cookie(self, cookie: Union[dict, str], ):
        return self.__check_cookie(cookie)[0]

    def __get_cookie_cache(self, cookie: Union[dict, str], ):
        return self.__check_cookie(cookie)[1]

    def __get_cookie_tiktok(self, cookie: Union[dict, str], ):
        return self.__check_cookie_tiktok(cookie)[0]

    def __get_cookie_tiktok_cache(self, cookie: Union[dict, str], ):
        return self.__check_cookie_tiktok(cookie)[1]

    @staticmethod
    def __add_cookie(cookie: Union[dict, str], tiktok=False) -> Optional[str]:
        if tiktok:
            parameters = ()
        else:
            ## 做缓存
            parameters = (MsToken.get_real_ms_token(), TtWid.get_tt_wid(),)
            # parameters = ({'msToken': '3yXtvtJjMmxqs2YTR4UVeltKGY46EVzpeXqsr6dfv186P_AyPQTN6unUM506w2igTAd3CxSyW0P1zkyK0lpO2Ni3U1GDkc8BeHW9oBt8lRFqCaSGcMGnvL7VLA9zByo='}, {'ttwid': '1%7C6iRQM8H-5XAiNLFMF9ra9tLnv82pEI0x9Uvsp-i1vzY%7C1712990129%7C188f0b9003e807601b24548f0ab1f52d132b5cd10bfaa4c5faa223fb493c7a10'})
        if isinstance(cookie, dict):
            for i in parameters:
                if isinstance(i, dict):
                    cookie.update(i)
        elif isinstance(cookie, str):
            for i in parameters:
                if isinstance(i, dict):
                    cookie += cookie_dict_to_str(i)
            return cookie

    @staticmethod
    def __check_root_again(root: Path) -> Union[bool, Path]:
        if root.resolve().parent.is_dir():
            root.mkdir()
            return root
        return False


    def update_cookie(self) -> None:
        self.__update_cookie(
            self.headers,
            self.cookie_object.cookie,
            False)

    def __update_cookie(
            self,
            headers: dict,
            cookie: dict,
            tiktok=False) -> None:
        if cookie:
            self.__add_cookie(cookie, tiktok, )
            headers["Cookie"] = cookie_dict_to_str(cookie)

    def __update_cookie_data(self, data: dict) -> None:
        for i in ("cookie", "cookie_tiktok"):
            if c := data.get(i):
                setattr(
                    self,
                    i,
                    self.cookie_object.extract(c))
        self.update_cookie()
